/**
 * @fileOverview
 */

Karamu = Karamu || {};
Karamu.Controllers = Karamu.Controllers || {};

/**
 * Groups are displayed as categories in desktop view; as tabs in dashboard view. Tabs are sub-divided into columns, and
 * gadgets are stacked in the columns.
 *
 * @class
 * @requires jQuery, underscore.js, karamu.js
 * @param {Object} context the top-level controller object, e.g karamu
 */
Karamu.Controllers.Group = function(context){
    this.context = context;

    /**
     * @param {Object} group
     * @public
     */
    this.render = function(groupData, options){
        if (groupData.title == 'Favorites') {
            var group = this.context.tmpl('desktop-group-favorites', {
                id: 'group-' + groupData.id,
                title: groupData.title,
                icon: 'icon-star'
            });
            var groupLink = this.context.tmpl('desktop-group-link', {
                id: 'group-' + groupData.id,
                title: groupData.title
            });
            group.prependTo('#groups');
            groupLink.prependTo('#search-bar .group-links');
        } else {
            var group = this.context.tmpl('desktop-group', {
                id: 'group-' + groupData.id,
                title: groupData.title,
                icon: 'icon-th-large'
            });
            var groupLink = this.context.tmpl('desktop-group-link', {
                id: 'group-' + groupData.id,
                title: groupData.title
            });
            group.appendTo('#groups');
            groupLink.appendTo('#search-bar .group-links');

            // Hook up the tab icons and title editing behaviors; hide/show states of the icons and inputs are done in CSS
            $('.group-remove'          , group).on({click: this.removeElement}, this);
            $('.group-check button'    , group).on({click: this.rename       }, this);
            $('.group-check .cancel'   , group).on({click: this.cancel       }, this);
            $('.group-edit'            , group).on({click: this.edit         }, this);
            $('.group-name-input input', group).on({
                keyup: function(e){
                    if (e.which === 13) {
                        // capture the "enter" keystroke
                        e.data.rename(e);
                    }
                    if (e.which == 27) {
                        // capture the "escape" keystroke
                        e.data.cancel;
                    }
                }
            }, this);
        }
        group.on({
            dragover: this.dragover,
            drop: this.drop
        }, this);

        groupLink.on({
            click: this.scrollTo
        }, this);

        this.renderShortcuts(groupData);
    };

    /**
     * @param {Object} groupData
     * @public
     */
    this.renderShortcuts = function(groupData) {
        var element = '#group-' + groupData.id + ' .gadgets';
        $(element).empty();
        _.each(groupData.gadgets, function(gadget){
            this.context.controllers.shortcut.render(element, gadget);
        }, this);
    };

    /**
     * @param {?Object} e event object
     * @param {?Object} e.data usually a controller object, e.g karamu; if omitted, this is used
     * @public
     */
    this.scrollTo = function(e){
        e.preventDefault();
        var el = $($(e.currentTarget).attr('href'));
        $('#overlay').animate({
            scrollTop: el.offset().top - $('#overlay').offset().top + $('#overlay').scrollTop()
        }, 250);
    };

    /**
     * @param {?Object} e event object
     * @param {?Object} e.data usually a controller object, e.g karamu; if omitted, this is used
     * @public
     */
    this.edit = function(e) {
        e.preventDefault();
        var el = $($(e.currentTarget).parents('li.group')),
            title = $('.group-name', el).text();

        $('.group-name-input input', el).val(title)
        el.addClass('edit-mode');

        // Don't allow dragging of tab while editing title
        el.on({dragstart: this.fail});
    };

    /**
     * @param {?Object} e event object
     * @param {?Object} e.data usually a controller object, e.g karamu; if omitted, this is used
     * @public
     */
    this.rename = function(e) {
        e.preventDefault();
        var el = $($(e.currentTarget).parents('li.group')),
            id = el.attr('id').substr('group-'.length),
            title = $.trim($('.group-name-input input', el).val());

        // TODO: Better validation. This just checks for empty strings or all spaces.
        if (title.length > 0 && !/^\s+$/.test(title) && title.toLowerCase !== 'favorites') {
            $('.group-name', el).text(title);
            $('.group-links a[href="#group-' + id + '"]').text(title);
            $(e.data.context.models[e.data.context.view]).trigger('change-group', [id, {title: title}]);
        }

        el.removeClass('edit-mode');

        // Re-enable dragging of tab
        el.off({dragstart: this.fail});
    };

    /**
     * @param {?Object} e event object
     * @param {?Object} e.data usually a controller object, e.g karamu; if omitted, this is used
     * @public
     */
    this.cancel = function(e) {
        e.preventDefault();

        var el = $($(e.currentTarget).parents('li.group'));

        el.removeClass('edit-mode');

        // Re-enable dragging of tab
        el.off({dragstart: this.fail});
    };

    /**
     * @param {?Object} e event object
     * @public
     */
    this.fail = function(e){
        return false;
    }

    /**
     * @param {?Object} e event object
     * @param {?Object} e.data usually a controller object, e.g karamu; if omitted, this is used
     * @public
     */
    this.removeElement = function(e) {
        e.preventDefault();

        var el = $($(e.currentTarget).parents('li.group')),
            id = el.attr('id').substr('group-'.length);

        // Remove the group from the interface
        $('#group-' + id).remove();
        $('.group-links a[href="#group-' + id + '"]').remove();

        // Update the model
        $(e.data.context.models[e.data.context.view]).trigger('remove-group', id);
    };

    /**
     * @param {Array} list
     * @param {number} column
     * @param {Object} context the top-level controller object, e.g karamu
     */
    this.sort = function(list, column, context) {
        var titles = _.map($('.group .gadgets .gadget'), function(g){
            return $('.gadget-name', g).text();
        });
        titles.sort();
        return _.sortBy(list, function(g){
            return _.indexOf(titles, g.id);
        });
    };

    /**
     * @param {?Object} e event object
     * @public
     */
    this.dragover = function(e) {
        e.preventDefault();
        e.originalEvent.dataTransfer.effectAllowed = 'copyMove';
    };

    /**
     * @param {?Object} e event object
     * @public
     */
    this.drop = function(e) {
        e.stopPropagation();

        var gadget = $.extend(true, {}, e.data.context.models[e.data.context.view]._gadget, JSON.parse(e.originalEvent.dataTransfer.getData('Text'))),
            originGroupId = gadget.groupId,
            targetGroupId = $(e.currentTarget).attr('id').substr('group-'.length);

        // Make sure gadget was dragged in from the gadget store, or moved from another group.
        if (originGroupId == targetGroupId) {
            e.originalEvent.dataTransfer.dropEffect = 'none';
        } else {
            e.originalEvent.dataTransfer.dropEffect = _.isUndefined(originGroupId) ? 'move' : 'copy';

            // Make sure it doesn't already exist in this group (by checking the model)
            var targetGroup = _.where(e.data.context.models[e.data.context.view].groups, {id: targetGroupId})[0],
                gadgetFound = false;
            _.each(targetGroup.gadgets, function(targetGadget){
                if (targetGadget.thumbnail === gadget.thumbnail &&
                    targetGadget.title     === gadget.title &&
                    targetGadget.url       === gadget.url
                ) {
                    gadgetFound = true;
                }
            });

            if (!gadgetFound) {
                // If dragged in from another group (groupId is undefined), remove gadget from original group.
                if (_.isString(originGroupId) && originGroupId.length > 0) {
                    $('#group-' + originGroupId + ' ' + e.data.context.controllers.catalog.options.gadgetSelector).each(function(i, el){
                        if (gadget.thumbnail == $('img'         , el).attr('src') &&
                            gadget.title     == $('.gadget-name', el).text()      &&
                            gadget.url       == $('.gadget-link', el).attr('href')
                        ) {
                            $(el).trigger('dragend');
                            $(el).remove();
                            $(e.data.context.models[e.data.context.view]).trigger('remove-shortcut', [gadget, originGroupId]);
                        }
                    });
                }

                // Add gadget shortcut to the target group.
                $(e.data.context.models[e.data.context.view]).trigger('add-shortcut', [gadget, targetGroupId]);
            }
        }

        return false;
    };

    /**
     * @param {Object} e event object
     * @param {?Object} e.data usually a controller object, e.g karamu; if omitted, this is used
     * @param {Object} group a plain object
     * @public
     */
    this.add = function(e, group){
        e.data.models[e.data.view].groups.push(group);
        e.data.controllers.group.push({
            context: e.data,
            data: group,
            groupId: group.id,
            success: function(){
                if (console && console.log) console.log('Group "' + group.title + '" (' + group.id + ') added.');
                $(this.models[this.view]).trigger('change-groups');
            }
        });
    };

    /**
     * This is special use case, made for manually adding a "Favorites" group to the desktop.
     *
     * @param {string} title anything goes
     * @param {?Object} context e.g karamu
     * @public
     */
    this.addNamedGroup = function(title, context) {
        var group = $.extend(true, {}, context.models[context.view]._group);
        group.title = title;
        context.getGUID({
            context: context,
            success: function(xml){
                group.id = $('guid', xml).text();
            },
            error: function(){
                if (console && console.log) console.log('Error retrieving GUID.');
            },
            complete: function(){
                // Render, then update the model in the background
                this.controllers.group.render(group);
                $(this.models[this.view]).trigger('add-group', group);
            }
        });
    };

    /**
     * @param {Object} e event object
     * @param {Object} e.data the top-level controller object, e.g karamu
     * @param {string} groupId exactly what it sounds like
     * @param {?Object} groupData a plain object; if present, selective data substitution occurs
     * @public
     */
    this.change = function(e, groupId, groupData){
        for (var a = 0, b = e.data.models[e.data.view].groups.length; a < b; a++) {
            if (e.data.models[e.data.view].groups[a].id === groupId) {
                if (groupData) {
                    for (f in groupData) {
                        e.data.models[e.data.view].groups[a][f] = groupData[f];
                    }
                }
                var changedGroup = $.extend(true, {}, e.data.models[e.data.view].groups[a]);
                e.data.controllers.group.push({
                    context: e.data,
                    data: changedGroup,
                    groupId: changedGroup.id,
                    success: function(){
                        if (console && console.log) console.log('Group "' + changedGroup.title + '" (' + changedGroup.id + ') changed.');
                        $(this.models[this.view]).trigger('change-groups');
                    }
                });

                // Update the view
                // TODO I don't like these render calls being here, but am undecided where to move them properly.
                e.data.controllers.group.renderShortcuts(changedGroup);
                if (changedGroup.title == 'Favorites') {
                    e.data.controllers.desktop.renderSidebar('#sidebar', changedGroup);
                }
            }
        }
    };

    /**
     * @param {Object} e event object
     * @param {Object} e.data the top-level controller object, e.g karamu
     * @param {string} groupId exactly what it sounds like
     * @public
     */
    this.remove = function(e, groupId){
        for (var a = 0, b = e.data.models[e.data.view].groups.length; a < b; a++) {
            if (e.data.models[e.data.view].groups[a].id === groupId) {
                e.data.models[e.data.view].groups.splice(a, 1);
                e.data.controllers.group.trash({
                    context: e.data,
                    groupId: groupId,
                    success: function(){
                        if (console && console.log) console.log('Group (' + groupId + ') deleted.');
                        $(this.models[this.view]).trigger('change-groups');
                    }
                });
                b--;
            }
        }
    };

    /**
     * @param {?Object} args
     * @param {?Function} args.complete
     * @param {?Object} args.context the top-level controller object, e.g karamu; if omitted, the XHR object is used
     * @param {?Function} args.error
     * @param {?Function} args.success
     * @param {string} args.groupId
     * @public
     */
    this.fetch = function(args){
        $.ajax({
            cache: false,
            complete: args.complete,
            context: args.context,
            dataType: 'json',
            error: args.error,
            success: args.success,
            type: 'GET',
            url: args.context.services.group.replace('%data%', args.groupId)
        });
    };

    /**
     * @param {?Object} args
     * @param {?Function} args.complete
     * @param {?Object} args.context the top-level controller object, e.g karamu; if omitted, the XHR object is used
     * @param {?Object} args.data
     * @param {?Function} args.error
     * @param {?Function} args.success
     * @param {string} args.groupId
     * @public
     */
    this.push = function(args){
        $.ajax({
            cache: false,
            complete: args.complete,
            contentType: 'application/json',
            context: args.context,
            data: JSON.stringify(args.data),
            error: args.error,
            success: args.success,
            type: 'PUT',
            url: args.context.services.group.replace('%data%', args.groupId)
        });
    };

    /**
     * @param {?Object} args
     * @param {?Function} args.complete
     * @param {?Object} args.context the top-level controller object, e.g karamu; if omitted, the XHR object is used
     * @param {?Function} args.error
     * @param {?Function} args.success
     * @param {string} args.groupId
     * @public
     */
    this.trash = function(args){
        $.ajax({
            cache: false,
            complete: args.complete,
            context: args.context,
            error: args.error,
            success: args.success,
            type: 'DELETE',
            url: args.context.services.group.replace('%data%', args.groupId)
        });
    };

};